package se.ramfelt.psn.console;

import java.util.Collections;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.http.impl.client.DefaultHttpClient;

import se.ramfelt.psn.model.Friend;
import se.ramfelt.psn.model.PresenceFriendComparator;
import se.ramfelt.psn.model.TrophySummary;
import se.ramfelt.psn.web.UnauthorizedResponseException;
import se.ramfelt.psn.web.us.UsPlaystationNetwork;
import se.ramfelt.psn.web.us.UsPlaystationSite;

public class Main {

    private Options options = new Options();
    
    public Main() {
        createOptions();
    }
    
    @SuppressWarnings("static-access")
    private void createOptions() {
        options.addOption(OptionBuilder.withLongOpt("help").withDescription("prints this message").create("h"));
        options.addOption(OptionBuilder.withLongOpt("email").isRequired().hasArg().withArgName("email").withDescription("Email registered at PlayStation Network").create("e"));
        options.addOption(OptionBuilder.withLongOpt("password").isRequired().hasArg().withArgName("password").withDescription("PlayStation Network password").create("p"));
        options.addOption(OptionBuilder.withLongOpt("format").hasArg().withArgName("table/csv").withDescription("Format output (default is table)").create("f"));
        options.addOption(OptionBuilder.withLongOpt("test").hasOptionalArg().withArgName("retries count").withDescription("Verify login credentials") .create("t"));
        options.addOption(OptionBuilder.withLongOpt("proxy").hasArg().withArgName("server").withDescription("Proxy server") .create());
        options.addOption(OptionBuilder.withLongOpt("proxyPort").hasArg().withArgName("server port").withDescription("Proxy server port (default 8080)") .create());
        options.addOption(OptionBuilder.withLongOpt("ignoreSSL").withDescription("Ignores any SSL problems, this accept ANY https certificate and might be dangerous.").create());
    }

    public void printHelp() {
        new HelpFormatter() .printHelp("psn", options, true);
    }
    
    public void parseArguments(String[] args) {
        try {
            CommandLine line = new GnuParser().parse(options, args);
            if (line.hasOption("e") && line.hasOption("p")) {
                UsPlaystationNetwork network = new UsPlaystationNetwork(new UsPlaystationSite(createHttpClient(line)), line.getOptionValue("e"), line.getOptionValue("p"));
                
                if (line.hasOption("t")) {
                    testCredentials(network, line.getOptionValue("t", "3"));
                } else {
                    try {                        
                        network.login();
                        List<Friend> list = network.getFriendList();
                        Collections.sort(list, new PresenceFriendComparator());
                        String format = line.getOptionValue("f", "table");
                        if (format.equals("table")) {
                            showFriendsInTable(list);
                        } else if (format.equals("csv")) {
                            showFriendsInCsv(list);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } else if (line.hasOption("h")) {
                printHelp();
            } else {
                printHelp();
            }
        } catch (ParseException e) {
            printHelp();
        }
    }
    

    private void showFriendsInCsv(List<Friend> list) {
        System.out.println("OnlineId,Presence,Level,Platinum,Gold,Silver,Bronze,LastSeen,CurrentGame");
        for (Friend friend : list) {
            TrophySummary trophySummary = friend.getTrophySummary();
            if (trophySummary == null) {
                trophySummary = new TrophySummary();
            }
            System.out.println(String.format("%s,%s,%s,%s,%s,%s,%s,%s,%s", friend.getOnlineId(), friend.getPresence(), 
                    trophySummary.getLevel(), trophySummary.getPlatinum(), trophySummary.getGold(), trophySummary.getSilver(), trophySummary.getBronze(), 
                    friend.getLastSeenTimestamp(), fixNull(friend.getCurrentGame())));
        }
    }

    private void showFriendsInTable(List<Friend> list) {
        String formatString = "%1$20s %2$10s %3$s\n";
        System.out.printf(formatString, "Username", "Presence", "Current game");
        System.out.printf(formatString, "-------------------", "----------", "-------------");
        for (Friend friend : list) {
            TrophySummary trophySummary = friend.getTrophySummary();
            if (trophySummary == null) {
                trophySummary = new TrophySummary();
            }
            System.out.printf(formatString, friend.getOnlineId(), friend.getPresence(), fixNull(friend.getCurrentGame()));
        }
    }
    
    private String fixNull(String arg) {
        if (arg == null) {
            return "";
        }
        return arg;
    }

    private DefaultHttpClient createHttpClient(CommandLine line) {
        DefaultHttpClient client = new DefaultHttpClient();
        if (line.hasOption("proxy")) {
            if (line.getOptionValue("proxy") == null) {
                System.err.println("Proxy arguments requires an argument.");
                printHelp();
                System.exit(1);
            }
            org.apache.http.HttpHost proxy = new org.apache.http.HttpHost(line.getOptionValue("proxy"), Integer.parseInt(line.getOptionValue("proxyPort", "8080")));
            client.getParams().setParameter(org.apache.http.conn.params.ConnRoutePNames.DEFAULT_PROXY, proxy);
        }
        
        if (line.hasOption("ignoreSSL")) {
            client.getConnectionManager().getSchemeRegistry().register(new org.apache.http.conn.scheme.Scheme("https", new AcceptAnyCertificateSocketFactory(), 443));
        }
        return client;
    }
    
    
    private void testCredentials(UsPlaystationNetwork network, String optionValue) {
        try {
            int numberOfRetries = Integer.parseInt(optionValue);
            for (int i = 1; i <= numberOfRetries; i++) {
                try {
                    System.out.print("Testing credentials [" + i + "/" + numberOfRetries + "] - ");
                    network.login();
                    System.out.println("Success, retrieved " + network.getFriendList().size() + " friends.");
                    System.exit(0);
                } catch (UnauthorizedResponseException e) {
                    System.err.println("Invalid PlayStation network credentials.");
                    System.exit(1);
                } catch (Exception e) {
                    System.err.println(e.getMessage());
                    if (i == numberOfRetries) {
                        System.exit(2);
                    }
                }
            }
        } catch (NumberFormatException nfe) {
            System.err.println("Retry count argument ('" + optionValue + "') is not an integer.");
            System.exit(2);
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        new Main().parseArguments(args);
    }
}
